/**
 * 作者：致心 vue项目所有文件命名全部采用中划线分隔   typeOfUtils
 * 创建时间：2021/3/5 14:50
 */

/**
 * 引用检测
 */
class CitationDetection {
  private list: any[] = [] // 传入引用进行判断

  add(value) {
    if (typeOfUtils.isNotMyBaseType(value)) {
      this.list.push(value)
    }
  }

  has(value) {
    const find = this.list.find(i => i === value)
    return !!find
  }

  notHas(value) {
    return !this.has(value)
  }

  /**
   *
   * @param value
   * @param cit {CitationDetection}
   * @returns {boolean}
   */
  static isEmpty(value, cit) {
    if (cit.has(value)) {
      return false
    }
    if (typeOfUtils.isNull(value) || typeOfUtils.isUndefined(value)) {
      return true
    }
    if (typeOfUtils.isArray(value) && value.length === 0) {
      return true
    }
    if (typeOfUtils.isString(value) && value.length === 0) {
      return true
    }
    if (typeOfUtils.isNumber(value) && isNaN(value)) {
      return true
    }
    if (typeOfUtils.isObject(value)) {
      if (Object.keys(value).length === 0) {
        return true
      } else {
        const list = Object.keys(value)
        let count = 0
        list.forEach(i => {
          cit.add(value[i])
          if (CitationDetection.isEmpty(value[i], cit)) {
            count++
          }
        })
        return count === list.length
      }
    }
    return false
  }
}

export default class typeOfUtils {
  static isType(val) {
    return Object.prototype.toString.call(val)
  }

  static isString(value) {
    return typeOfUtils.isType(value) === '[object String]'
  }

  static isNotString(value) {
    return !typeOfUtils.isString(value)
  }

  static isNumber(value) {
    return typeOfUtils.isType(value) === '[object Number]'
  }

  static isNotNumber(value) {
    return !typeOfUtils.isNumber(value)
  }

  static isFloat(value) {
    if (typeOfUtils.isNotNumber(value)) {
      return false
    }
    const string = value.toString()
    return string.indexOf('.') !== -1
  }

  static isNotFloat(value) {
    return !typeOfUtils.isFloat(value)
  }

  static isBoolean(value) {
    return typeOfUtils.isType(value) === '[object Boolean]'
  }

  static isNotBoolean(value) {
    return !typeOfUtils.isBoolean(value)
  }

  static isSymbol(value) {
    return typeOfUtils.isType(value) === '[object Symbol]'
  }

  static isNotSymbol(value) {
    return !typeOfUtils.isSymbol(value)
  }

  static isUndefined(value) {
    return typeOfUtils.isType(value) === '[object Undefined]'
  }

  static isNotUndefined(value) {
    return !typeOfUtils.isUndefined(value)
  }

  static isNull(value) {
    return typeOfUtils.isType(value) === '[object Null]'
  }

  static isNotNull(value) {
    return !typeOfUtils.isNull(value)
  }

  static isFunction(value) {
    return typeOfUtils.isType(value) === '[object Function]'
  }

  static isNotFunction(value) {
    return !typeOfUtils.isFunction(value)
  }

  static isDate(value) {
    return typeOfUtils.isType(value) === '[object Date]'
  }

  static isNotDate(value) {
    return !typeOfUtils.isDate(value)
  }

  static isArray(value) {
    return typeOfUtils.isType(value) === '[object Array]'
  }

  static isNotArray(value) {
    return !typeOfUtils.isArray(value)
  }

  static isRegExp(value) {
    return typeOfUtils.isType(value) === '[object RegExp]'
  }

  static isNotRegExp(value) {
    return !typeOfUtils.isRegExp(value)
  }

  static isError(value) {
    return typeOfUtils.isType(value) === '[object Error]'
  }

  static isNotError(value) {
    return !typeOfUtils.isError(value)
  }

  static isHTMLDocument(value) {
    try {
      // @ts-ignore
      if (HTMLElement === 'object') {
        return value instanceof HTMLElement;
      } else {
        return value && typeof value === 'object' && value.nodeType === 1 && typeof value.nodeName === 'string';
      }
    } catch (e) {
      return value === window || value === document
    }
  }

  static isNotHTMLDocument(value) {
    return !typeOfUtils.isHTMLDocument(value)
  }

  static isGlobal(value) {
    return typeOfUtils.isType(value) === '[object global]'
  }

  static isNotGlobal(value) {
    return !typeOfUtils.isGlobal(value)
  }

  static isObject(value) {
    return typeOfUtils.isType(value) === '[object Object]'
  }

  static isNotObject(value) {
    return !typeOfUtils.isObject(value)
  }

  static isPromise(value) {
    return typeOfUtils.isType(value) === '[object Promise]'
  }

  static isNotPromise(value) {
    return !typeOfUtils.isPromise(value)
  }

  /**
   * @Description :判断值是不是空，判断为空的数据有 [],{}'',NaN,null,Undefined
   * @Author :致心
   * @Date :2021/3/10 17:01
   * @param value
   * @returns {boolean}
   */
  static isEmpty(value) {
    const cit = new CitationDetection()
    return CitationDetection.isEmpty(value, cit)
  }

  /**
   * @Description :判断值是不是不为空
   * @Author :致心
   * @Date :2021/3/31 10:19
   * @param value
   * @returns {boolean}
   */
  static isNotEmpty(value) {
    return !typeOfUtils.isEmpty(value)
  }

  /**
   * @Description :判断对象里面多个属性不为空
   * @Author :致心
   * @Date :2021/4/29 15:41
   * @param value {Object}
   * @param attrs {Array} 传入字符数组
   * @returns {boolean}
   */
  static objectAttrsNotEmpty(value, attrs) {
    if (typeOfUtils.isNotObject(value)) {
      throw new Error('传入的value不是对象')
    }
    if (typeOfUtils.isNotArray(attrs)) {
      throw new Error('传入的attrs不是数组')
    }
    if (typeOfUtils.isEmpty(value)) {
      throw new Error('传入的value不能为空')
    }
    if (typeOfUtils.isEmpty(attrs)) {
      throw new Error('传入的attrs不能为空')
    }
    let count = 0
    attrs.forEach(i => {
      if (typeOfUtils.isString(i)) {
        count++
      }
    })
    if (count !== attrs.length) {
      throw new Error('传入的attrs有数据错误')
    }
    count = 0
    attrs.forEach(i => {
      if (typeOfUtils.isNotEmpty(value[i])) {
        count++
      }
    })
    return count === attrs.length
  }

  /**
   * @Description :判断对象里面多个属性有空
   * @Author :致心
   * @Date :2021/4/30 15:03
   * @param value {Object}
   * @param attrs {Array} 传入字符数组
   * @returns {boolean}
   */
  static objectAttrsHasEmpty(value, attrs) {
    return !typeOfUtils.objectAttrsNotEmpty(value, attrs)
  }

  /**
   * instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上。
   * @param left
   * @param right
   * @returns {boolean}
   */
  static instanceof(left, right) {
    // eslint-disable-next-line no-proto
    let implicitPrototype = left.__proto__
    const displayPrototype = right.prototype
    while (implicitPrototype) {
      if (implicitPrototype === displayPrototype) return true
      // eslint-disable-next-line no-proto
      implicitPrototype = implicitPrototype.__proto__
    }
    return false
  }

  /**
   * 我的基础类型判断
   * @param val
   * @return {boolean}
   */
  static isMyBasetype(val) {
    return !(typeOfUtils.isArray(val) ||
      typeOfUtils.isFunction(val) ||
      typeOfUtils.isDate(val) ||
      typeOfUtils.isPromise(val) ||
      typeOfUtils.isObject(val))
  }

  /**
   * 我的基础类型判断
   * @param val
   * @return {boolean}
   */
  static isNotMyBaseType(val) {
    return !typeOfUtils.isMyBasetype(val)
  }
}
